home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PsL Monthly 1993 December
/
PSL Monthly Shareware CD-ROM (December 1993).iso
/
prgmming
/
dos
/
c
/
rng.exe
/
RAN.C
< prev
next >
Wrap
C/C++ Source or Header
|
1991-12-19
|
15KB
|
474 lines
/* ran.c - functions to initialize random number generator and random number
** generator itself.
**
** Module entry points
** -------------------
**
** setup() ----> N.B. MUST be called before irand() or poiss()
** ran() ----> macro defined in ran.h
** irand()
** poiss()
** binom()
**
**
** Either RAN_KNU or RAN_MAR must be defined before compilation, but
** only one can be defined. If RAN_KNU is defined, a subtractive
** generator from volume two of Knuth is used. If RAN_MAR is defined,
** a more complex generator discovered by Marsaglia is used. Knuth's
** generator is about 50% faster than Marsaglia's, but its sequence is
** not as random. For many purposes, Knuth's generator is good enough.
** It is much better than the generators normally supplied with C compilers,
** and it is often faster.
**
** setup() must be called before irand() is called. irand() does
** NOT check to see that the array it uses has been initialized.
**
** irand() returns a uniformly distributed integer in [0..NO_NUMBERS)
** when NO_ZERO is not defined and in (0..NO_NUMBERS) when NO_ZERO is
** defined.
**
** The macro ran() (defined in ran.h) returns a uniformly distributed
** random number in the range [0,1) when NO_ZERO is not defined and in
** (0,1) when NO_ZERO is defined. The resolution is 1/CONV_REAL.
**
** Strictly speaking poiss() should only be called when NO_ZERO is not
** defined. But the error will be very small, since it will have an
** effect on the results in only 1 out of NO_NUMBERS calls, on average.
**
**
** References:
**
** Knuth, D. E. 1981. The Art of Computer Programming. Vol. 2,
** Seminumerical Algorithms. Addison-Wesley, Reading Mass.
**
**
** Author: Kent E. Holsinger
**
** Revision history
** ----------------
**
** Version 1.0 -- 24 January 1990
** Original version of Knuth additive random number generator
**
** Version 1.1 -- 24 January 1990
** ran() changed to macro operating on long
**
** Version 2.1 -- 31 December 1990
** Added Marsaglia generator with conditional compilation
**
** Version 2.3 -- 18 December 1991
** Converted both generators to use unsigned longs
** Editorial changes to internal comments
**
**
*/
#include <stdio.h>
#include <math.h>
#include "ran.h"
/* constants used in random number generator */
#ifdef RAN_KNU
#define D 21
#define LASTDIM 55
#define FIRSTDIM 24
#endif
#ifdef RAN_MAR
#define CD 7654321
#define CM 16777213
#define LASTDIM 98
#define FIRSTDIM 34
#endif
/*
** array of integers used in random number
** generator
*/
typedef unsigned long INTARRAY[LASTDIM];
typedef unsigned long *POINTER;
/* file used to store random number seed */
FILE *randfile;
/* LOCAL VARIABLE DEFINITIONS */
static INTARRAY x; /* array used to generate random nos. */
static POINTER j_ptr, k_ptr; /* pointers into array */
#ifdef RAN_MAR
static long c;
#endif
static char MOD_ID[] = "%W% %D%";
/* LOCAL FUNCTION PROTOTYPES */
#ifdef __STDC__
static void rknuin(unsigned long m);
static void rmarin(int ij, int kl);
#else
static void rknuin();
static void rmarin();
#endif
/***************************************************************************/
/* */
/* MODULE ENTRY POINT */
/* setup() */
/* */
/***************************************************************************/
/* Initialize the random number generator */
#ifdef __STDC__
void setup(unsigned long *seed)
#else
void setup(seed)
unsigned long *seed;
#endif
{
#ifdef RAN_MAR
unsigned long temp; /* to write seed to file for next call */
int ij, kl; /* seeds for Marsaglia generator */
#endif
randfile = fopen("RAND.INT", "r");
#ifdef RAN_KNU
if (!randfile) {
printf("Enter a random number seed: ");
scanf("%lu", seed);
} else {
fscanf(randfile, "%lu", seed);
fclose(randfile);
}
do {
if (*seed == 0) {
fprintf(stderr, "Seed must be greater than zero\n");
fprintf(stderr, "Enter a random number seed: ");
scanf("%lu", seed);
}
} while (*seed == 0);
rknuin(*seed);
randfile = fopen("RAND.INT", "w");
fprintf(randfile, "%lu", x[LASTDIM - 1]);
fclose(randfile);
#endif
#ifdef RAN_MAR
if (!randfile) {
printf("Enter first random number seed: ");
scanf("%d", &ij);
printf("Enter second random number seed: ");
scanf("%d", &kl);
} else {
fscanf(randfile, "%lu", seed);
fclose(randfile);
ij = (int) (*seed) % 31328;
kl = (int) ((*seed >> 16) % 30081);
}
if (ij < 0 || ij > 31328 || kl < 0 || kl > 30081) {
do {
fputs("The first random number seed must have a value between 0\
and 31328.\n", stderr);
fputs("The second seed must have a value between 0 and 30081.\n",
stderr);
printf("Enter first random number seed: ");
scanf("%d", &ij);
printf("Enter second random number seed: ");
scanf("%d", &kl);
} while (ij < 0 || ij > 31328 || kl < 0 || kl > 30081);
}
rmarin(ij, kl);
temp = ((x[LASTDIM - 1] >> 16) % 30081) << 16;
temp += x[LASTDIM - 1] % 31328;
randfile = fopen("RAND.INT", "w");
fprintf(randfile, "%lu", temp);
fclose(randfile);
#endif
}
/***************************************************************************/
/* */
/* MODULE ENTRY POINT */
/* irand() */
/* */
/***************************************************************************/
/* Returns a random number in [0,NO_NUMBERS) */
/* if NO_ZERO left undefined */
/* Returns a random number in (0,NO_NUMBERS) */
/* when NO_ZERO is defined */
#ifdef RAN_KNU
/* Knuth generator */
/*
* This is a direct translation of the subtractive generator (p. 171),
* which is based on the additive generator described on p. 27. It has
* a period greater than 2^55 - 1.
*
* This implementation takes advantage of the fact that all elements of
* the array are themselves random numbers to return the element in the
* array currently pointed to. Strict implementation of the algorithm
* would require definition of a temporary variable to hold the return
* value, which properly is *j_ptr before it is decremented
*
* On a Zenith Z-386 each call requires about 13 microseconds with
* NO_ZERO defined. Without NO_ZERO defined each call requires about
* 11 microseconds. (Results obtained using Microsoft C v6.00a,
* optimizing with /Ozx
*/
#ifdef __STDC__
unsigned long irand(void)
#else
unsigned long irand()
#endif
{
*j_ptr -= *k_ptr;
j_ptr--;
k_ptr--;
if (j_ptr < x)
j_ptr = &x[LASTDIM - 1];
else if (k_ptr < x)
k_ptr = &x[LASTDIM - 1];
#ifndef NO_ZERO
return *j_ptr; /* simply return *j_ptr for U in [0,1) */
#else
if (*j_ptr == 0)
return irand();
else
return *j_ptr;
#endif
}
#endif
#ifdef RAN_MAR
/* Marsaglia generator */
/*
* C This random number generator originally appeared in "Toward a Universal
* C Random Number Generator" by George Marsaglia and Arif Zaman. C Florida
* State University Report: FSU-SCRI-87-50 (1987)
* C
* C It was later modified by F. James and pu